package com.google.code.opengw.server;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.google.code.opengw.server.deployment.DeploymentManager;
import com.google.code.opengw.server.domain.Domain;
import com.google.code.opengw.server.initializer.SystemInitializer;
import com.google.code.opengw.server.support.FileUtils;
import com.google.code.opengw.support.OpenGWException;
import com.google.code.opengw.support.OpenGWLogger;

public class Server {

	// DEFAULT CONFIGURATION VALUES
	private int connectionQueueSize = 4096;
	private int initialWorkerCount = 5;
	private int workerIncreasementCount = 5;
	private int maxWorkerCount = 100;
	private int connectionTimeout = 10000;
	private int connectionIdleTimeout = 1000;
	private int readTimeout = 60000;
	private int readBufferSize = 1024;
	private int connectionBufferSize = 1024;
	private long deploymentControlInterval = 15000;
	private boolean allowParameters = false;
	private boolean allowPathExtension = false;
	private String availableMethods = "GET,POST";

	private String keystorePassword = "changeit";

	private boolean systemActive;
	
	private final List<String> domainNames;
	private final HashMap<String, Domain> domains;
	private final DeploymentManager deploymentManager;

	private static Server server;
	
	private final String configPath;
	private final String deployPath;
	private final String domainPath;
	
	public static final String CONFIG_FILE_NAME = "config.xml";
	public static final String USER_LIST_FILE_NAME = "user-list.xml";
	public static final String PUBLISH_FILE_NAME = "publish.xml";
	public static final String KEYSTORE_FILE_NAME = "keystore.ks";
	public static final String LOG4J_LOG_PATH = System.getProperty("log4jLogPath", "bin" + System.getProperty("file.separator") + "log" + System.getProperty("file.separator") + "server.log");

	static {
		try{
			server = new Server();
		} catch (Exception e) {
			OpenGWLogger.addError(Server.class, e.getMessage(), e);
			System.exit(1);
		}
	}

	private Server() throws OpenGWException {
		domains = new HashMap<String, Domain>();
		domainNames = new ArrayList<String>();
		deploymentManager = new DeploymentManager(deploymentControlInterval);
		configPath = System.getProperty("configPath", "bin" + System.getProperty("file.separator") + "config"+ System.getProperty("file.separator"));
		deployPath = System.getProperty("deployPath", "bin" + System.getProperty("file.separator") + "deploy" + System.getProperty("file.separator"));
		domainPath = System.getProperty("domainPath", "bin" + System.getProperty("file.separator") + "domains" + System.getProperty("file.separator"));

		
		if(!FileUtils.checkDirectoryExists(configPath))
			throw new OpenGWException(OpenGWException.CONFIG_FILE_NOT_FOUND);
		if(!FileUtils.checkDirectoryExists(deployPath))
			throw new OpenGWException(OpenGWException.DEPLOY_DIRECTORY_NOT_FOUND);
		if(!FileUtils.checkDirectoryExists(domainPath))
			throw new OpenGWException(OpenGWException.DOMAIN_DIRECTORY_NOT_FOUND);
	}

	public static Server getInstance() {
		return server;
	}

	public synchronized void initialize() throws OpenGWException {

		SystemInitializer initiator = SystemInitializer.getInstance();
		initiator.initialize();
	}
	
	public void startServer() throws OpenGWException{
		systemActive = true;
		Thread deploymentControlThread = new Thread(deploymentManager);
		deploymentControlThread.start();
		for(String domainName : domainNames){
			startDomain(domainName);
		}
	}
	
	public void stopServer() throws IOException, OpenGWException{
		systemActive = false;
		deploymentManager.setSystemActive(systemActive);
		for(String domainName : domainNames){
			stopDomain(domainName);
		}
	}
	
	public void startDomain(String domainName) throws OpenGWException{
		getDomain(domainName).startDomain();
	}
	
	public void stopDomain(String domainName) throws IOException, OpenGWException{
		getDomain(domainName).stopDomain();
	}
	
	
	public Domain getDomain(String domainName)
			throws OpenGWException {
		Domain domain = domains.get(domainName);
		if (domain == null)
			throw new OpenGWException(OpenGWException.format(
					OpenGWException.DOMAIN_NOT_FOUND, domainName));

		return domain;
	}
	
	public List<String> getDomainNames(){
		return domainNames;
	}
	
	public void putDomain(Domain domain) throws OpenGWException{
		if(!domains.containsKey(domain.getName())){
			domains.put(domain.getName(), domain);
			domainNames.add(domain.getName());
			OpenGWLogger.addInfo(SystemInitializer.class, "Domain initialized successfully: " + domain.toString());
		} else {
			throw new OpenGWException(OpenGWException.format(OpenGWException.DOMAIN_ALREADY_EXIST, domain.getName()));
		}
	}
	
	public boolean domainExist(String domainName){
		return domains.containsKey(domainName);
	}
	
	public File getConfigXML(){
		return new File(configPath + CONFIG_FILE_NAME);
	}
	
	public File getUserListXML(){
		return new File(configPath + USER_LIST_FILE_NAME);
	}
	
	public File getKeystore(){
		return new File(configPath + KEYSTORE_FILE_NAME);
	}
	
	public File getPublishDirectory(String domainName, String publishName){
		return new File(domainPath + domainName + System.getProperty("file.separator") + publishName + System.getProperty("file.separator"));
	}
	
	public File getPublishResourcePath(String domainName, String publishName, String resourceURL){
		return new File(domainPath + domainName + System.getProperty("file.separator") + publishName + System.getProperty("file.separator") + resourceURL);
		
	}

	public int getConnectionQueueSize() {
		return connectionQueueSize;
	}

	public void setConnectionQueueSize(int connectionQueueSize) {
		this.connectionQueueSize = connectionQueueSize;
	}

	public int getInitialWorkerCount() {
		return initialWorkerCount;
	}

	public void setInitialWorkerCount(int initialWorkerCount) {
		this.initialWorkerCount = initialWorkerCount;
	}

	public int getWorkerIncreasementCount() {
		return workerIncreasementCount;
	}

	public void setWorkerIncreasementCount(int workerIncreasementCount) {
		this.workerIncreasementCount = workerIncreasementCount;
	}

	public int getMaxWorkerCount() {
		return maxWorkerCount;
	}

	public void setMaxWorkerCount(int maxWorkerCount) {
		this.maxWorkerCount = maxWorkerCount;
	}

	public int getConnectionTimeout() {
		return connectionTimeout;
	}

	public void setConnectionTimeout(int connectionTimeout) {
		this.connectionTimeout = connectionTimeout;
	}

	public int getConnectionIdleTimeout() {
		return connectionIdleTimeout;
	}

	public void setConnectionIdleTimeout(int connectionIdleTimeout) {
		this.connectionIdleTimeout = connectionIdleTimeout;
	}

	public int getReadTimeout() {
		return readTimeout;
	}

	public void setReadTimeout(int readTimeout) {
		this.readTimeout = readTimeout;
	}

	public int getReadBufferSize() {
		return readBufferSize;
	}

	public void setReadBufferSize(int readBufferSize) {
		this.readBufferSize = readBufferSize;
	}

	public int getConnectionBufferSize() {
		return connectionBufferSize;
	}

	public void setConnectionBufferSize(int connectionBufferSize) {
		this.connectionBufferSize = connectionBufferSize;
	}

	public boolean isAllowParameters() {
		return allowParameters;
	}

	public void setAllowParameters(boolean allowParameters) {
		this.allowParameters = allowParameters;
	}

	public boolean isAllowPathExtension() {
		return allowPathExtension;
	}

	public void setAllowPathExtension(boolean allowPathExtension) {
		this.allowPathExtension = allowPathExtension;
	}

	public String getAvailableMethods() {
		return availableMethods;
	}

	public void setAvailableMethods(String availableMethods) {
		this.availableMethods = availableMethods;
	}

	public String getKeystorePassword() {
		return keystorePassword;
	}

	public void setKeystorePassword(String keystorePassword) {
		this.keystorePassword = keystorePassword;
	}

	public boolean isSystemActive() {
		return systemActive;
	}

	public void setSystemActive(boolean systemActive) {
		this.systemActive = systemActive;
	}

	public long getDeploymentControlInterval() {
		return deploymentControlInterval;
	}

	public void setDeploymentControlInterval(long deploymentControlInterval) {
		this.deploymentControlInterval = deploymentControlInterval;
	}

	public HashMap<String, Domain> getDomains() {
		return domains;
	}

	public String getConfigPath() {
		return configPath;
	}

	public String getDeployPath() {
		return deployPath;
	}

	public String getDomainPath() {
		return domainPath;
	}
	
	
}
